package com.lee.hystrix.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class ReflectionUtil {
	
	/**
	 * 通过调用无参构造函数返回实例对象
	 * @param clazz
	 * @return
	 */
	public static <T> T getInstance(Class<T> clazz) {
		try {
			return clazz.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 通过构造器调用有参构造函数
	 * @param constructor
	 * @param objects
	 * @return
	 */
	public static <T> T getInstance(Constructor<T> constructor, Object ... objects ) {
		try {
			return constructor.newInstance(objects);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 根据name（属性名）搜索属性，迭代搜索所有父类(父类的父类，以此类推)
	 * @param clazz
	 * @param name 
	 * @return
	 */
	public static Field findField(Class<?> clazz, String name) {
		return findField(clazz, name, null);
	}
	
	/**
	 * 根据name（属性名）或type（属性类型）搜索属性，迭代搜索所有父类(父类的父类，以此类推)
	 * @param clazz
	 * @param name 当type存在时，可空
	 * @param type 当name存在时，可空
	 * @return
	 */
	public static Field findField(Class<?> clazz, String name, Class<?> type) {
		Assert.notNull(clazz, "Class不能为空");
		Assert.isTrue(name != null || type != null, "name和type不能同时为空");
		Class<?> searchType = clazz;
		while (!Object.class.equals(searchType) && searchType != null) {//搜索到父类为Object为止
			Field[] fields = searchType.getDeclaredFields();
			for (Field field : fields) {
				if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
					return field;
				}
			}
			searchType = searchType.getSuperclass();
		}
		return null;
	}

	public static Object getField(Object obj, String name, Class<?> type) {
		Field field = findField(obj.getClass(), name, type);
		field.setAccessible(true);
		try {
			return field.get(obj);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 尝试根据方法名搜索对应的方法，如果方法有重载，将返回第一个匹配的，迭代搜索所有父类
	 * @param clazz
	 * @param name
	 * @return
	 */
	public static Method findMethod(Class<?> clazz, String name) {
		return findMethod(clazz, name, null);
	}
	
	/**
	 * 根据方法名和参数类型搜索对应的方法，迭代搜索所有父类
	 * @param clazz
	 * @param name
	 * @param paramTypes
	 * @return
	 */
	public static Method findMethod(Class<?> clazz, String name, Class<?> ... paramTypes) {
		Assert.notNull(clazz, "Class不能为空");
		Assert.notNull(name, "方法名不能为空");
		Class<?> searchType = clazz;
		while (searchType != null) {
			Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods());
			for (Method method : methods) {
				if (name.equals(method.getName())
						&& (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
					return method;
				}
			}
			searchType = searchType.getSuperclass();
		}
		return null;
	}
	
	public static Object invokeMethod(Method method, Object target) {
		return invokeMethod(method, target, new Object[0]);
	}
	
	public static Object invokeMethod(Method method, Object target, Object ... args) {
		try {
			return method.invoke(target, args);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static <T> Constructor<T> findDeclaredConstructor(Class<T> clazz, Class<?> ... parameterTypes) {
		try {
			return clazz.getDeclaredConstructor(parameterTypes);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 返回属性对应的getter方法，采用驼峰命名方法，如属性username返回getUsername,userName返回getUserName
	 * @param propertyName
	 * @return
	 */
	public static String getMethodName(String propertyName) {
		Objects.requireNonNull(propertyName, "参数不能为空");
		if (propertyName.length() < 1) {
			return "";
		}
		return "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
	}
	
	/**
	 * 获取指定属性的类型
	 * @param clazz
	 * @param name 属性名
	 * @return
	 */
	public static Class<?> getFieldType(Class<?> clazz, String name) {
		Field field = findField(clazz, name);
		return field.getType();
	}
	
	/**
	 * 返回该类的所有类注解
	 * @param clazz
	 * @return
	 */
	public static Annotation[] getAnnotations(Class<?> clazz) {
		return clazz.getAnnotations();
	}
	
	/**
	 * 返回该类是否包含相应的注解，所有批评才返回true
	 * @param clazz
	 * @param annotations
	 * @return
	 */
	public static boolean hasAnnotations(Class<?> clazz, Annotation ... annotations) {
		Annotation[] allAnnotations = getAnnotations(clazz);
		List<Annotation> asList = Arrays.asList(allAnnotations);
		for (Annotation annotation : annotations) {
			if (!asList.contains(annotation)) {
				return false;
			}
		}
		return true;
	}
	
	public static boolean hasSpecificAnnotation(Class<?> clazz, Class<? extends Annotation> annotation) {
		return clazz.isAnnotationPresent(annotation);
	}
}